package file_tab

import (
	"strconv"
	"strings"

	"file_explorer/common/log"
	"file_explorer/common/util"
	"file_explorer/model"
	"file_explorer/service"
	"file_explorer/service/eventbus"
	"file_explorer/service/third_program"
	"file_explorer/view/packed_widgets"
	"file_explorer/view/store"

	"fyne.io/fyne/v2"
	"github.com/samber/lo"
	"github.com/spf13/cast"
)

type FileList struct {
	*packed_widgets.Table

	tabCtx *store.FileTabContext
}

// 从context回写CheckList状态，用于排序后还原选中项
func (fileList *FileList) reloadCheckList() {
	if len(fileList.tabCtx.CheckedList) == 0 {
		return
	}
	checkItemSet := lo.SliceToMap(fileList.tabCtx.CheckedList, func(item *model.FileInfo) (*model.FileInfo, struct{}) {
		return item, struct{}{}
	})
	checkIndexList := lo.FilterMap(fileList.tabCtx.FileInfos, func(item *model.FileInfo, index int) (int, bool) {
		if _, ok := checkItemSet[item]; ok {
			return index, true
		}
		return 0, false
	})
	fileList.Checklist.SetCheckIndexList(checkIndexList)
	log.Debug("fileTab fileList checklist reloaded, checkNum: %d, path: %s", len(checkIndexList), fileList.tabCtx.GetPath())
}

func NewFileList(tabCtx *store.FileTabContext) *FileList {
	fileList := &FileList{
		tabCtx: tabCtx,
		Table:  packed_widgets.NewTable(),
	}

	fileList.Table.SetGetData(func(index int) interface{} {
		return tabCtx.FileInfos[index]
	})
	fileList.Table.SetGetDataLength(func() int {
		return len(tabCtx.FileInfos)
	})

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("name").
			SetHeader("名称").
			SetWidth(200).
			SetIconGetter(func(data interface{}) fyne.Resource {
				return data.(*model.FileInfo).GetIcon()
			}).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).Name }).
			SetOrderBy(func(asc bool) {
				fileList.tabCtx.SetSortFunc(
					util.GetComparatorByPriority[*model.FileInfo](
						asc,
						func(a, b *model.FileInfo) int {
							return int(b.Type) - int(a.Type)
						},
						func(a, b *model.FileInfo) int {
							return util.CmpText(a.Name, b.Name)
						},
					),
				)
			}).
			SetDefaultOrder(),
	)

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("size").
			SetHeader("大小").
			SetWidth(80).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).GetSize() }).
			SetOrderBy(func(asc bool) {
				fileList.tabCtx.SetSortFunc(
					util.GetComparatorByPriority[*model.FileInfo](
						asc,
						func(a, b *model.FileInfo) int {
							return int(b.Type) - int(a.Type)
						},
						func(a, b *model.FileInfo) int {
							return int(b.Size - a.Size)
						},
						func(a, b *model.FileInfo) int {
							return util.CmpText(a.Name, b.Name)
						},
					),
				)
			}),
	)

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("modify_time").
			SetHeader("修改时间").
			SetWidth(150).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).GetModifyTime() }).
			SetOrderBy(func(asc bool) {
				fileList.tabCtx.SetSortFunc(
					util.GetComparatorByPriority[*model.FileInfo](
						asc,
						func(a, b *model.FileInfo) int {
							return int(b.Type) - int(a.Type)
						},
						func(a, b *model.FileInfo) int {
							return int(b.ModifyTime.Unix() - a.ModifyTime.Unix())
						},
						func(a, b *model.FileInfo) int {
							return util.CmpText(a.Name, b.Name)
						},
					),
				)
			}),
	)

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("score").
			SetHeader("评分").
			SetWidth(60).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).GetScoreStr() }).
			SetOrderBy(func(asc bool) {
				fileList.tabCtx.SetSortFunc(
					util.GetComparatorByPriority[*model.FileInfo](
						asc,
						func(a, b *model.FileInfo) int {
							return int(b.Type) - int(a.Type)
						},
						func(a, b *model.FileInfo) int {
							return b.GetScore() - a.GetScore()
						},
						func(a, b *model.FileInfo) int {
							return util.CmpText(a.Name, b.Name)
						},
					),
				)
			}).
			SetDoubleTappedExpand(),
	)

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("tag").
			SetHeader("标签").
			SetWidth(100).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).GetTagsStr() }).
			SetDoubleTappedExpand(),
	)

	fileList.Table.AddColumn(
		packed_widgets.NewTableColumn().
			SetKey("note").
			SetHeader("备注").
			SetWidth(300).
			SetTextGetter(func(data interface{}) string { return data.(*model.FileInfo).GetNote() }).
			SetDoubleTappedExpand(),
	)

	fileList.Table.Build()

	// 双击文件
	fileList.Table.OnDoubleTapped = func(row int, col int) {
		fileInfo := fileList.tabCtx.FileInfos[row]
		if fileInfo.IsDir() {
			// 进入目录
			log.Info("打开目录：%s", fileInfo.GetPath())
			fileList.tabCtx.OpenDir(fileInfo.GetPath())
		} else {
			// 打开文件
			log.Info("打开文件：%s", fileInfo.GetPath())
			third_program.GetCaller().OpenFile(fileInfo.GetPath())
		}
	}

	// 排序时同步checklist
	fileList.Table.RegisterConsumer(eventbus.EventKeyWidgetTableOrderChange, func(event *model.Event) {
		log.Info("文件列表排序变更，路径：%s", fileList.tabCtx.GetPath())
		fileList.reloadCheckList()
	})

	// 文件列表checklist更新时同步到tab context内
	fileList.Table.Checklist.RegisterConsumer(eventbus.EventKeyWidgetChecklistChange, func(event *model.Event) {
		checkIndexList := fileList.Table.Checklist.GetCheckIndexList()
		fileList.tabCtx.CheckedList = make([]*model.FileInfo, 0, len(checkIndexList))
		for _, checkIndex := range checkIndexList {
			fileList.tabCtx.CheckedList = append(fileList.tabCtx.CheckedList, fileList.tabCtx.FileInfos[checkIndex])
		}
		log.Info("文件列表选中项变更，路径：%s", fileList.tabCtx.GetPath())
	})

	// 快捷修改元数据
	fileList.Table.RegisterConsumer(eventbus.EventKeyWidgetTableExpandCellEntrySubmit, func(event *model.Event) {
		fileInfo := event.Msg["data"].(*model.FileInfo)
		key := event.Msg["key"].(string)
		text := strings.TrimSpace(event.Msg["text"].(string))

		switch key {
		case "score":
			if len(text) == 0 {
				fileInfo.SetScore(nil)
			} else if score, err := strconv.Atoi(text); err == nil && cast.ToString(score) != fileInfo.GetScoreStr() {
				fileInfo.SetScore(&score)
			}
		case "tag":
			fileInfo.SetTags(lo.Uniq(lo.Map(strings.Split(text, ";"), func(tag string, _ int) string {
				return strings.TrimSpace(tag)
			})))
		case "note":
			fileInfo.SetNote(&text)
		default:
			return
		}

		metafileName, err := service.UpdateLatestMetadata(fileInfo)
		if err != nil {
			packed_widgets.NotifyError("更新元数据失败，路径：%s，错误：%v", fileInfo.GetPath(), err)
			return
		}

		log.Info("更新元数据成功，路径：%s，元文件名：%s", fileInfo.GetPath(), metafileName)

		tabCtx.RefreshPath()
	})

	return fileList
}
